// Copyright (c) 2023 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package handler

import (
	"strings"
	"time"

	cfg "github.com/alibaba/higress/plugins/wasm-go/extensions/jwt-header/config"
	"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
	jwt "github.com/golang-jwt/jwt/v5"
	"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
	"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
)

type ErrDenied struct {
	msg    string
	denied func() types.Action
}

func (e *ErrDenied) Error() string {
	return e.msg
}

// jwt-auth 插件认证逻辑与 basic-auth 一致：
// - global_auth == true 开启全局生效：
//   - 若当前 domain/route 未配置 allow 列表，即未配置该插件：则在所有 consumers 中查找，如果找到则认证通过，否则认证失败 (1*)
//   - 若当前 domain/route 配置了该插件：则在 allow 列表中查找，如果找到则认证通过，否则认证失败
//
// - global_auth == false 非全局生效：(2*)
//   - 若当前 domain/route 未配置该插件：则直接放行
//   - 若当前 domain/route 配置了该插件：则在 allow 列表中查找，如果找到则认证通过，否则认证失败
//
// - global_auth 未设置：
//   - 若没有一个 domain/route 配置该插件：则遵循 (1*)
//   - 若有至少一个 domain/route 配置该插件：则遵循 (2*)
//
// https://github.com/alibaba/higress/blob/e09edff827b94fa5bcc149bbeadc905361100c2a/plugins/wasm-go/extensions/basic-auth/main.go#L191
func OnHTTPRequestHeaders(ctx wrapper.HttpContext, config cfg.JWTTokenConfig, log wrapper.Log) types.Action {
	s := config.Subject
	header := &proxywasmProvider{}
	start := 0
	for {
		// 查找 ${ 起始位置
		startIdx := strings.Index(s[start:], "${")
		if startIdx == -1 {
			break // 无更多占位符，退出
		}
		startIdx += start

		// 查找 } 结束位置
		endIdx := strings.Index(s[startIdx:], "}")
		if endIdx == -1 {
			break // 格式错误，退出
		}
		endIdx += startIdx

		// 提取 xxx（如 name）
		key := s[startIdx+2 : endIdx] // +2 跳过 ${
		sval := extractToken(key, header, log)
		// 替换单个占位符
		s = s[:startIdx] + sval + s[endIdx+1:] // +1 跳过 }
		start = endIdx + 1                     // 更新搜索起始点
	}
	log.Info("jwt-header --- build jwt, subject: " + s)
	claims := jwt.RegisteredClaims{
		// A usual scenario is to set the expiration time relative to the current time
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(config.Expire))),
		IssuedAt:  jwt.NewNumericDate(time.Now()),
		NotBefore: jwt.NewNumericDate(time.Now()),
		Issuer:    config.Issuer,
		Subject:   s,
		Audience:  []string{config.Audience},
	}
	// 创建 token 对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 生成完整签名
	auth, err := token.SignedString([]byte(config.Secret))
	if err != nil {
		log.Error("gen jwt token err")
		return types.ActionContinue
	}
	if h := config.ForHeader; h != nil {
		proxywasm.AddHttpRequestHeader(config.ForHeader.Name, config.ForHeader.ValuePrefix+auth)
	}
	log.Info("jwt-header --- build jwt, token: " + auth)
	return types.ActionContinue
}

func contains(str string, arr []string) bool {
	for _, i := range arr {
		if i == str {
			return true
		}
	}
	return false
}
